#!/usr/bin/env bash
# vi:syntax=sh


APP_NAME="learn_to_code"
# This is used while developing to supply the `GODOT_VERSION` variable that is 
# normally provided by the CI
local_godot_version=${GODOT_VERSION:-"3.4.4"}


######################################## HELP


help(){
  if [ "$1" == "help" ]; then
    echo "Shows a short and helpful help text"
    exit 0
  fi
  echo ""
  echo "Builder Script for Learn To Code With Godot"
  echo "please use one of the following commands:"
  for i in ${!funcs[@]}; do
    command=${funcs[i]}
    echo "  - ${command//_/:}"
  done
  echo "you can get more info about commands by writing ./run <command> help"
  echo "you dry run most (but not all) commands by setting the \$dry env variable"
  echo ""
}


######################################## CLEAN


clean_web(){
  if [ "$1" == "help" ]; then
    echo "Removes web build"
    exit 0
  fi

  echo "rm -rf \"${dirs[web]}\""
  test "$dry" || rm -rf "${dirs[web]}"
  echo "✓ Removed web files"
}


clean_all(){
  if [ "$1" == "help" ]; then
    echo "Removes all build directories"
    exit 0
  fi
  
  echo "rm -rf build"
  test "$dry" || rm -rf build
  echo "mkdir -p build"
  test "$dry" || mkdir -p build
  echo "touch build/.gdignore"
  test "$dry" || touch build/.gdignore
  echo "✓ Removed all builds"
}


clean(){
  if [ "$1" == "help" ]; then
    echo "Removes one or all build directories"
    exit 0
  fi
  
  echo "choose what to clean"
  exit 1
}


######################################## WEB DEV HELPERS


web_server(){
  if [ "$1" == "help" ]; then
    echo "Runs a python webserver in build/web"
    exit 0
  fi
  if ! command -v python3 &> /dev/null; then
      echo ""
      echo "ERROR: Python 3 isn't available. It is required to run the server"
      echo " - in Debian/Ubuntu: sudo apt-get -y install inotify-tools"
      echo " - in Arch/Manjaro: sudo pacman --needed --noconfirm -S python"
      echo " - in Fedora: sudo dnf install python3"
      echo ""
      exit 1
  fi
  echo "python3 -m http.server 8000 --directory \"${dirs[web]}\""
  test "$dry" || python3 -m http.server 8000 --directory "${dirs[web]}"
}


web_watch(){
  if [ "$1" == "help" ]; then
    echo "Watches a directory for changes. Recompiles Godot when a \.gd file changes;"
    echo "Overwrites JS and CSS files in the static directory when they change"
    exit 0
  fi
  if ! command -v inotifywait &> /dev/null; then
      echo ""
      echo "ERROR: inotifywait isn't available. It is required to run the server"
      echo "this command is usually part of the package inotify-tools"
      echo " - in Debian/Ubuntu: sudo apt-get -y install inotify-tools"
      echo " - in Arch/Manjaro: sudo pacman --needed --noconfirm -S inotify-tools"
      echo " - in Fedora: sudo dnf install inotify-tools"
      echo ""
      exit 1
  fi
  last_time_compiled=0
  throttle_compile=10
  last_time_copied=0
  throttle_copy=1
  source_dir=${1:-.}
  full_path=$(readlink -f $source_dir)
  _update_file(){
    if [ "$base_dir" = "html_export" ]; then
      if (($now - $last_time_copied >= $throttle_copy)); then
        if [[ "$filename" == "index_template.html" ]]; then
          prepare_htmltemplate
          changed_file="./html_export/index.html"
          filename="index.html"
        fi
        echo "cp \"$changed_file\" \"${dirs[web]}/$filename\""
        cp "$changed_file" "${dirs[web]}/$filename"
      fi
      last_time_copied=$(date +%s)
    else
      echo "Changes detected in $changed_file, will recompile"
      if (($now - $last_time_compiled >= $throttle_compile)); then
        export_custom "${presets[web]}" "${dirs[web]}/index.html"
      fi 
      last_time_compiled=$(date +%s)
    fi
  }
  echo "will watch $full_path"
  inotifywait --monitor --recursive --quiet --event modify,move,create,delete --format '%w%f' "$source_dir" | while read changed_file
  do
    changed_dir=$(dirname "$changed_file")
    base_dir=$(echo "$changed_dir" | cut -d "/" -f2)
    ext=${changed_file##*.}
    now=$(date +%s)
    filename=$(basename "$changed_file")
    ext=$(echo "$ext" |  tr '[:upper:]' '[:lower:]')
    exclude=0
    include=0
    if [[ -d "$$changed_file" ]] || [[ "$filename" =~ ^\. ]] || [[ "$base_dir" == "build" ]]; then
      exclude=1
    fi
    if [[ "$exclude" -eq 0 ]] && [[ $ext =~ gd|tscn|tres|js|css|html ]]; then
      include=1
    fi
    if [[ "$exclude" -eq 0 ]] && [[ "$include" -eq 1 ]]; then
      _update_file
    fi
  done
}


web_debug(){
  if [ "$1" == "help" ]; then
    echo "Exports the web build, starts a python webserver, and watches"
    echo "the directory for changes. Require Python 3 and inotifywait to"
    echo "be in your \$PATH"
    exit 0
  fi
  echo "You need the custom templates. Please make sure to run prepare:local at least once!"
  debug=true
  clean_web
  export_web
  web_server &
  web_watch
}


######################################## EXPORTS


export_custom(){
  if [ "$1" == "help" ]; then
    echo "Exports a Godot build"
    echo "Usage:"
    echo "./run export:any <name> <output_path>"
    echo ""
    echo "for example: ./run export:any \"HTML5\" \"build/web/index.html\""
    echo ""
    echo "<name> needs to be a valid export specified in \`export_presets.cfg\`"
    echo "set the environment variable \$debug to export a debug build"
    exit 0
  fi
  export_name=$1
  output=$2
  dir=$(dirname "$output")
  mkdir -p "$dir"
  prepare_versionfile
  # THERE IS NO DIFFERENCE BETWEEN A DEBUG AND A RELEASE BUILD 
  # we export a debug build in all cases, due to https://github.com/GDQuest/learn-gdscript/issues/618
  # we could remove the `debug` keyword, but it is kept both for backwards
  # compatibility, and in case we solve this differently in the future
  # in which case, all that needs to be done is replce `export-debug` with `export`
  if [ -z "$debug" ]; then
    echo "godot --quiet --no-window --export-debug \"$export_name\" \"$output\""
    test "$dry" || godot --quiet --no-window --export-debug "$export_name" "$output"
  else
    echo "godot --quiet --no-window --export-debug \"$export_name\" \"$output\""
    test "$dry" || godot --quiet --no-window --export-debug "$export_name" "$output"
  fi
  echo "✓ Exported \`$export_name\` build to \`$dir\`"
}

export_web(){
  if [ "$1" == "help" ]; then
    echo "Exports the Web build and copies over static files"
    echo "You can specify the following environment variables:"
    echo " - \`\$url\` for the html template"
    echo " - \`\$git_branch\` for deploying to a subfolder (the" 
    echo "   \`release\` branch will be blanked, and deployed to"
    echo "   the root subfolder)"
    exit 0
  fi
  prepare_htmltemplate
  
  export_custom "${presets[web]}" "${dirs[web]}/index.html"
  
  echo "cp -r html_export/static/* \"${dirs[web]}\""
  test "$dry" || cp -r html_export/static/* "${dirs[web]}"

  echo "✓ Copied web files to ${dirs[web]}"
}


export_osx(){
  if [ "$1" == "help" ]; then
    echo "Exports the OSX build"
    exit 0
  fi
  export_custom "${presets[osx]}" "${dirs[osx]}/$APP_NAME.zip"
}


export_windows(){
  if [ "$1" == "help" ]; then
    echo "Exports the Windows build"
    exit 0
  fi
  export_custom "${presets[windows]}" "${dirs[windows]}/$APP_NAME.exe"
}


export_linux(){
  if [ "$1" == "help" ]; then
    echo "Exports the Linux build"
    exit 0
  fi
  export_custom "${presets[linux]}" "${dirs[linux]}/$APP_NAME.x86_64"
}


export_all(){
  if [ "$1" == "help" ]; then
    echo "exports all builds"
    exit 0
  fi
  export_linux
  export_windows
  export_osx
  export_web
}


######################################## PUSHING TO ITCH


push_custom(){
  if [ "$1" == "help" ]; then
    echo "Usage:"
    echo "push <build> [...<tags>]"
    echo "the build needs to exist in the build/ directory"
    echo "the release will automatically be tagged with the branch name"
    echo ""
    echo "example:"
    echo "run push linux mytag"
    echo "available builds:"
    for build in "${builds[@]}" ; do
      if [ ! -d "$dir" ]; then
        echo "  - $build: UNAVAILABLE (export it first)"
      else
        echo "  - $build"
      fi
    done
    exit 0
  fi
  
  current_build="$1"
  dir="${dirs[$current_build]}"
  if [ -z "$current_build" ]; then
    echo "You need to specify a build to push"
    exit 1
  fi
  if [ ! -d "$dir" ]; then
    echo "$dir does not exist. Maybe you forgot to export first?"
    exit 1
  fi
  test "$dry" || _verify_itch_variables_or_die
  old="$IFS"
  IFS='-'
  all_tags="$*-$git_branch"
  IFS=$old
  
  if [ ! -z "$dry" ] && [ -z "$ITCHIO_USERNAME" ]; then
    ITCHIO_USERNAME="user"
    ITCHIO_GAME="game"
  fi

  echo "butler push \"$dir\" \"$ITCHIO_USERNAME/$ITCHIO_GAME:$all_tags\""
  test "$dry" || butler push "$dir" "$ITCHIO_USERNAME/$ITCHIO_GAME:$all_tags"
}


push_web(){
  if [ "$1" == "help" ]; then
    echo "Pushes the Web build"
    exit 0
  fi
  push_custom "web"
}


push_osx(){
  if [ "$1" == "help" ]; then
    echo "Pushes the OSX build"
    exit 0
  fi
  push_custom "osx"
}


push_windows(){
  if [ "$1" == "help" ]; then
    echo "Pushes the Windows build"
    exit 0
  fi
  push_custom "windows"
}


push_linux(){
  if [ "$1" == "help" ]; then
    echo "Pushes the Linux build"
    exit 0
  fi
  push_custom "linux"
}


push_all(){
  if [ "$1" == "help" ]; then
    echo "Pushes all builds to Itch"
    exit 0
  fi
  push_linux
  push_windows
  push_osx
  push_web
}


######################################## CI/CD


prepare_ci_copytemplates(){
  if [ "$1" == "help" ]; then
    echo "-- DEPRECATED --"
    echo "Do not use this, use gettemplates instead"
    echo "We need to download custom templates to enable offline GDScript error reporting"
    echo "Once/if this is merged in master, we can use copytemplates again"
    echo "----------------"
    echo "Prepares the CI by copying the template from the docker"
    echo "You should not run this on your local computer"
    echo "cannot run in dry mode"
    exit 0
  fi
  if [ -z "$GODOT_VERSION" ] || [ "$GODOT_VERSION" == "" ]; then
    echo "\$GODOT_VERSION environment variable is not set"
    exit 1
  fi
  echo "Will use templates for ${GODOT_VERSION}"
  echo "mkdir -v -p ~/.local/share/godot/templates"
  mkdir -v -p ~/.local/share/godot/templates
  echo "✓ created local template directory"
  echo "mv /root/.local/share/godot/templates/${GODOT_VERSION}.stable ~/.local/share/godot/templates/${GODOT_VERSION}.stable"
  mv /root/.local/share/godot/templates/${GODOT_VERSION}.stable ~/.local/share/godot/templates/${GODOT_VERSION}.stable
  echo "✓ copied templates"
}


prepare_ci_gettemplates(){
  if [ "$1" == "help" ]; then
    echo "Prepares the CI by getting the custom templates online"
    echo "cannot run in dry mode"
    exit 0
  fi
  if [ -z "$GODOT_VERSION" ] || [ "$GODOT_VERSION" == "" ]; then
    echo "\$GODOT_VERSION environment variable is not set"
    exit 1
  fi
  echo "Will get the custom templates for ${GODOT_VERSION}"
  echo "wget https://github.com/Razoric480/godot/releases/download/${GODOT_VERSION}-parser/$(sed 's/\.//g' <<< ${GODOT_VERSION})custom-templates.zip"
  wget https://github.com/Razoric480/godot/releases/download/${GODOT_VERSION}-parser/$(sed 's/\.//g' <<< ${GODOT_VERSION})custom-templates.zip || {
    echo "Templates for \`${GODOT_VERSION}\` not found"
    exit 1
  }
  echo "✓ Downloaded templates"
  echo "Will unzip templates"
  echo "unzip $(sed 's/\.//g' <<< ${GODOT_VERSION})custom-templates.zip -d templates"
  unzip $(sed 's/\.//g' <<< ${GODOT_VERSION})custom-templates.zip -d templates
  echo "✓ copied templates"
}


prepare_ci_software(){
  if [ "$1" == "help" ]; then
    echo "Prepares the CI by installing the necessary software"
    echo "You should not run this on your local computer"
    echo "cannot run in dry mode"
    exit 0
  fi
  echo "apt-get update"
  apt-get update
  echo "apt-get install -y --no-install-recommends sed rsync"
  apt-get install -y --no-install-recommends sed rsync
  echo "✓ installed software"
}


prepare_overridefile(){
    if [ "$1" == "help" ]; then
    echo "Copies the override file if it exists"
    echo "You should not run this on your local computer"
    exit 0
  fi
  override_file=$1
  if [ ! -z "$override_file" ]; then
    if [ -f "$override_file" ]; then
      echo "cp ${{override_file}} override.cfg"
      test "$dry" || cp ${{override_file}} override.cfg
      echo "✓ copied override file"
    else
      echo "override \`$override_file\` was specified, but not found"
      exit 1
    fi
  else
    echo "✓ no config override to copy"
  fi
}


prepare_ci(){
  if [ "$1" == "help" ]; then
    echo "Prepares the CI"
    echo "You should not run this on your local computer"
    echo "cannot run in dry mode"
    exit 0
  fi
  #prepare_ci_copytemplates
  prepare_ci_gettemplates
  prepare_ci_software
  prepare_overridefile $@
  echo "✓ CI prepared"
}


prepare_versionfile(){
  if [ "$1" == "help" ]; then
    echo "Creates and fills the version file"
    echo "cannot run in dry mode"
    exit 0
  fi
  changed_file="utils/version.gd"
  echo "# AUTO GENERATED changed_file, YOUR CHANGES WILL NOT REMAIN" > $changed_file
  echo "class_name AppVersion" >> $changed_file
  echo "" >> $changed_file
  echo "const version := \"$version\";" >> $changed_file
  echo "const git_commit := \"$git_commit\";" >> $changed_file
  echo "const git_branch := \"$git_branch\";" >> $changed_file
  echo "const build_date := [$build_date];" >> $changed_file
  echo "const build_date_unix := $build_date_unix;" >> $changed_file
  echo "const build_date_iso := \"$build_date_iso;\"" >> $changed_file

  echo "✓ created version file"
}


prepare_htmltemplate(){
  if [ "$1" == "help" ]; then
    echo "Creates the HTML template. This is a mandatory step"
    echo "before exporting to web"
    echo "cannot run in dry mode"
    exit 0
  fi
  sed "s@>branch and commit hash<@>${watermark}<@" html_export/index_template.html > html_export/index.html
  sed -i "s@GDQUEST_ENVIRONMENT = {}@GDQUEST_ENVIRONMENT = {\n \
     build_date: \"$build_date\",\n \
     build_date_iso: \"$build_date_iso\",\n \
     build_date_unix: $build_date_unix,\n \
     git_branch: \"$git_branch\",\n \
     git_commit: \"$git_commit\",\n \
     version: \"$version\",\n \
     url: \"$url\",\n \
   }@" html_export/index.html
  sed -i "s@%url%@$url@" html_export/index.html
  echo "✓ generated html template"
}


prepare_local(){
  # TODO: remove this if/when prepare_ci_gettemplates becomes unecessary
  if [ "$1" == "help" ]; then
    echo "Downloads the custom templates locally"
    echo "Necessary before any export"
    echo "cannot run in dry mode"
    exit 0
  fi
  GODOT_VERSION="$local_godot_version" prepare_ci_gettemplates
}


######################################## BOOTSTRAP


_verify_itch_variables_or_die(){
  error=0
  if [ ! -d "$dir" ] && [ "$tag" != "all" ]; then
    echo "\`$dir\` does not seem to exist. Did you type the name correctly?"
    error=1
  fi
  if [ -z "$BUTLER_API_KEY" ]; then
    echo "\$BUTLER_API_KEY is a mandatory variable to push to itch"
    error=1
  fi
  if [ -z "$ITCHIO_USERNAME" ]; then
    echo "\$ITCHIO_USERNAME is a mandatory variable to push to itch"
    error=1
  fi
  if [ -z "$ITCHIO_GAME" ]; then
    echo "\$ITCHIO_GAME is a mandatory variable to push to itch"
    error=1
  fi
  if ! command -v butler &> /dev/null; then
    echo "\`butler\` is not installed or not available on \$PATH"
    error=1
  fi
  if [ "$error" -eq "1" ]; then
    exit 1
  fi
}


_prepare_build_vars(){
  builds=("linux" "windows" "osx" "web")
  build_date=$(date '+%Y, %m, %d, %H, %M, %S')
  build_date_iso=$(date "+%Y/%m/%d %H:%M:%S")
  build_date_unix=$(date '+%s')
  git_commit=$(git rev-parse HEAD)
  if [ -z "$git_branch" ]; then
    git_branch=$(git branch --show-current)
  fi

  should_use_branch=true; [ "$git_branch" == "release" ] && should_use_branch=false

  if [ -z "$version" ]; then
    version=$(git describe --tags --abbrev=0 --match "*.*.*")
  fi
  watermark="$build_date_iso | $git_branch:$version:$git_commit"
  if [ "$should_use_branch" = false ]; then
    watermark="version: $version"
  fi
  if [ -z "$url" ]; then
    url=${1:-"https://gdquest.github.io/learn-gdscript"}
    if [ "$should_use_branch" = true ]; then
      url="$url/$git_branch"
    fi
  fi

  presets_file="export_presets.cfg"
  if [ ! -f "$presets_file" ]; then
    echo "\`$presets_file\` not found"
    exit 1
  fi

  declare -g -A presets
  presets[linux]="Linux/X11"
  presets[windows]="Windows Desktop"
  presets[web]="HTML5"
  presets[osx]="Mac OSX"

  for preset in "${presets[@]}" ; do
      if ! grep -q "name=\"$preset\"" "$presets_file"; then
        echo "Preset \`$preset\` not found in $presets_file"
        exit 1
      fi
  done

  declare -g -A dirs
  dirs[linux]="build/linux"
  dirs[windows]="build/windows"
  dirs[osx]="build/osx"
  dirs[web]="build/web"
  
  if [ "$should_use_branch" = true ]; then
    dirs[web]="${dirs[web]}/$git_branch"
  fi

  echo "✓ created all build variables"
}

_bootstrap(){
  funcs=(`declare -F | awk '{print $NF}' | sort | egrep -v "^_"`)
  command=${1//:/_}

  if [[ " ${funcs[*]} " =~ " ${command} " ]]; then
    shift
    if [ "$command" != "help" ]; then
      _prepare_build_vars
    fi
    $command $@
    exit 0
  else
    if [[ $command ]]; then
      echo ""
      echo "\`${command}\` isn't a recognized command"
      echo ""
    fi
    help
    exit 1
  fi
}

_bootstrap $@
